﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace IcqSharp
{
    public static class Session
    {
        public static UInt32 LocalUIN;
        public static String Password;

        public static Boolean UseSSL = false;

        public static Boolean ConnectionLost = false;

        public static Boolean IdleAllow = true;


        public static Boolean hello = true;
        //public static Byte[] cookie;

        public static UInt16 LocalSequence;

        public static Boolean SsiEnabled = true;


        public static Boolean AimEnabled = true;
        public static Boolean AvatarsEnabled = false;
        public static Boolean xStatusEnabled = true;

        public static Boolean WebAwareEnabled = true;
        public static Byte DcType = 1;

        public static UInt32 SrvLastUpdate;
        public static UInt16 SrvRecordCount;

        public static Boolean NewServerReady;
        public static String NewServer;
        public static Boolean NewServerSSL;
        public static Byte[] CookieData;

        public static DateTime MemberSince;
        public static DateTime LogonSince;

        public static UInt32 ExternalIP;

        

        public static Boolean LoggedIn = false;
        public static Boolean IsMigrating = false;
        public static Boolean ReinitRecver = false;

        public static IcqStatus Status;
        public static IcqStatus DesiredStatus;
        public static Boolean IcqOnline = false;
        public static Byte xStatus = 0;

        public static UInt16 CookieSeq;

        public static TcpClient client;
        public static NetworkStream stream;

        public static Byte SlowSend = 1;

        public static void SendPacket(Packet p)
        {
            // make sure to have the connection handle
            //connectionHandleMutex->Enter();

            //if (hServerConn)
            //{
            //int nSendResult;

            // This critsec makes sure that the sequence order doesn't get screwed up
            ///localSeqMutex->Enter();

            // :IMPORTANT:
            // The FLAP sequence must be a WORD. When it reaches 0xFFFF,
            // it should wrap to 0x0000, otherwise we'll get kicked by server.
            LocalSequence++;
            if (LocalSequence == 0xFFFF)
                LocalSequence = 0x0000;

            // get all data
            var data = p.Data.ToArray();

            // pack sequence number
            data[2] = (Byte)((LocalSequence & 0xFF00) >> 8);
            data[3] = (Byte)((LocalSequence & 0x00FF));

            // pack actual length
            var len = (UInt16)(p.Data.Length - 6);
            data[4] = (Byte)((len & 0xFF00) >> 8);
            data[5] = (Byte)((len & 0x00FF));

            stream.Write(data);

            //localSeqMutex->Leave();
            //connectionHandleMutex->Leave();

            // Send error
            //if (nSendResult == SOCKET_ERROR)
            //{
            //    icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), LPGEN("Your connection with the ICQ server was abortively closed"));
            //    icq_serverDisconnect(FALSE);

            //    if (m_iStatus != ID_STATUS_OFFLINE)
            //    {
            //        SetCurrentStatus(ID_STATUS_OFFLINE);
            //    }
            //}
            //else
            //{ // Rates management
            //    icq_lock l(m_ratesMutex);
            //    m_rates->packetSent(pPacket);
            //}

            //}
            //else
            //{
            //    connectionHandleMutex->Leave();
            //    NetLog_Server("Error: Failed to send packet (no connection)");
            //}

            //SAFE_FREE((void**)&pPacket->pData);
        }

        public static Thread ProcessMessagesThread;
        public static void ProcessMessages()
        {
            Int32 marker = 0, channel = 0;
            UInt16 sequence, len = 0;

            while (true)
            {
                // get marker (1 byte, 0x2A)
                do
                {
                    try { marker = stream.ReadByte(); }
                    catch { Debugger.Break(); return; }
                }
                while (marker != 0x2A);

                // get channel, sequence, len (5 bytes)
                while (client.Available < 5) { Thread.Sleep(10); }
                try
                {
                    channel = (Byte)stream.ReadByte();
                    sequence = stream.ReadUInt16();
                    len = stream.ReadUInt16();
                }
                catch { Debugger.Break(); return; }

                // get data
                while (client.Available < len) { Thread.Sleep(10); }
                var data = new MemoryStream(stream.ReadBytes(len));

                // handle
                switch (channel)
                {
                    case (Byte)Channel.Login: LoginChannelHandler.Handle(data); break;
                    case (Byte)Channel.Data: DataChannelHandler.Handle(data); break;
                    case (Byte)Channel.Error: ErrorChannelHandler.Handle(data); break;
                    case (Byte)Channel.Close: CloseChannelHandler.Handle(data); break;
                    case (Byte)Channel.Ping: PingChannelHandler.Handle(data); break;
                    default: Debugger.Break(); break;
                }

            }
        }

        public static void ConnectToNewServer()
        {
            // get the ip and port
            var host = Session.NewServer.Split(':')[0];
            var port = UInt16.Parse(Session.NewServer.Split(':')[1]);

            // connect
            client = new TcpClient();
            client.Connect(host, port);
            if (client.Connected)
            {
                stream = client.GetStream();
                Session.ReinitRecver = true;
                //return client;
            }

            //return null;
        }

        public static UInt32 Port
        {
            get
            {
                try { return (UInt32)(client.Client.LocalEndPoint as IPEndPoint).Port; }
                catch { return 0; }
            }
        }




    }
}
